package edu.cmsc611.textsimulator;
import java.util.*;
import java.lang.*;
import java.awt.*;

import edu.cmsc611.textsimulator.Instruction.regType;
import edu.cmsc611.textsimulator.Registers.registerMode;

/**
 * Execute handles the processing of the execute stage of pipelined execution.
 */
class Execute extends Stage {

	public Stages stg;
	OutputChart tablechart=null;
	FunctionalUnitSingleton funit=null;
	Instruction tempinstr=null;
	int numOfInsReadyToWB=0;
	Memory myMemory;
	MemoryType mainMemory;
	boolean secondblock=false;
	Vector instructions;

	boolean finishedAdd=false,finishedMult=false,finishedDiv,finishedInt=false,finishedIntUnit2=false;
	/**
	 * creates an Execute object.
	 */
	public Execute() {
		super();

	}

	/**
	 * Processes the current instruction.  Implements the execute stage of a pipelined processor.
	 * Uses a vector of instructions, the Fetch stage and Decode stage, as well as the ListBoxes.
	 * @see Instruction
	 * @see Fetch
	 * @see Decode
	 * @see ListBoxes
	 */
	public void step(Vector Instructions, Fetch myFetch, Decode myDecode, DispInfo lb,Stages _stg) {
		String str;

		mainMemory = new MemoryType();
		myMemory = new Memory();
		Registers reg=Registers.getInstance();
		stg=_stg;
		Instruction tempadderinst,tempmultinst,tempdivinst,tempintinst;
		numOfInsReadyToWB=0;
		this.instructions=Instructions;
		/*
		 * set flag if any unit finished execution
		 */
		finishedAdd=false;
		finishedMult=false;
		finishedDiv=false;
		finishedInt=false;
		finishedIntUnit2=false;
		/*
		 * insert the instruction including NOP to respected queue and keep track of instructions
		 * elapse time. if the size of queue is is equal then deque the item and pass to next stage.
		 * from the global queue, we can insert the instruction and insert until it is full,if full then pop up the
		 * first instruction. if it not NOP then proceed.
		 */

		tempinstr=myInstruction;
		funit=FunctionalUnitSingleton.getInstance();

		updateCycles();
		passCompletedTask();
		int rval;
		switch(myInstruction.opcode) 
		{

		case 38: // MUL.D -- 8 bit
			myInstruction.rdValue = (myInstruction.rsValue & 0x00FF) *
			(myInstruction.rtValue & 0x00FF);


			myInstruction.setCycleElapse(1);
			funit.getFpMultiplier().add(myInstruction);
			/*
			 * if not execution finished then add NOP
			 */
			if(!finishedAdd && !finishedMult && !finishedDiv && !finishedInt && !finishedIntUnit2)
				tempinstr=new Instruction("NOP");

			break;
		case 50: // DIV.D -- 8 bit
			myInstruction.rdValue = (myInstruction.rsValue & 0x00FF) /
			(myInstruction.rtValue & 0x00FF);

			myInstruction.setCycleElapse(1);
			funit.getFpDivider().add(myInstruction);

			/*
			 * if not execution finished then add NOP
			 */
			if(!finishedAdd && !finishedMult && !finishedDiv && !finishedInt && !finishedIntUnit2)
				tempinstr=new Instruction("NOP");
			break;
		case 36: // ADD.D
			myInstruction.rdValue = myInstruction.rsValue + myInstruction.rtValue;

			//			if(funit.getFpAdder().size()!=0){
			//				if(funit.getFpAdder().peek().getCycleElapse()==funit.getFpAdderReqCycles())
			//				{
			//					tempadderinst=funit.getFpAdder().poll();
			//
			//				}
			//			}
			myInstruction.setCycleElapse(1);
			funit.getFpAdder().add(myInstruction);

			/*
			 * if not execution finished then add NOP
			 */
			if(!finishedAdd && !finishedMult && !finishedDiv && !finishedInt && !finishedIntUnit2)
				tempinstr=new Instruction("NOP");

			break;
		case 51: // SUB.D
			myInstruction.rdValue = myInstruction.rsValue - myInstruction.rtValue;

			myInstruction.setCycleElapse(1);
			funit.getFpAdder().add(myInstruction);

			/*
			 * if not execution finished then add NOP
			 */
			if(!finishedAdd && !finishedMult && !finishedDiv && !finishedInt && !finishedIntUnit2)
				tempinstr=new Instruction("NOP");
			break;
		case 48: // LW
		case 56: //L.D
		case 58: //S.W
		case 54: // SW
			// rdValue becomes the memory address with which to load/store.
			// For SW, rtValue is the value to store into memory. R1 0(R4) value of R4+0
			/*if(myInstruction.opcode==48||myInstruction.opcode==56){
			rval=reg.readRegisterR(myInstruction.rt)+myInstruction.immediate;//-reg.readRegisterR(myInstruction.rt);
			reg.setWritetoRegisterR(myInstruction.rd, rval);
			}
			if(myInstruction.opcode==58||myInstruction.opcode==54){
				rval=reg.readRegisterR(myInstruction.rd);//-reg.readRegisterR(myInstruction.rt);
				reg.setWritetoRegisterR(myInstruction.rt+myInstruction.immediate, rval);
				}*/
			//myInstruction.rdValue = myInstruction.rsValue + myInstruction.immediate;
			//int key=reg.readRegisterR(myInstruction.rs) + myInstruction.immediate;
			myInstruction.setCycleElapse(1);
			funit.getIntUnit2().add(myInstruction);
			if(!finishedAdd && !finishedMult && !finishedDiv && !finishedInt && !finishedIntUnit2)
				tempinstr=new Instruction("NOP");
			break;



		case 24: // DSUB
			rval=reg.readRegisterR(myInstruction.rs)-reg.readRegisterR(myInstruction.rt);
			reg.setWritetoRegisterR(myInstruction.rd, rval);
			//myInstruction.rdValue = myInstruction.rsValue << myInstruction.rtValue;
			myInstruction.setCycleElapse(1);
			funit.getIntUnit().add(myInstruction);

			/*
			 * if not execution finished then add NOP
			 */
			if(!finishedAdd && !finishedMult && !finishedDiv && !finishedInt && !finishedIntUnit2)
				tempinstr=new Instruction("NOP");
			break;
		case 26: // DADD
			rval=reg.readRegisterR(myInstruction.rs)+reg.readRegisterR(myInstruction.rt);
			reg.setWritetoRegisterR(myInstruction.rd, rval);
			//myInstruction.rdValue = myInstruction.rsValue >> myInstruction.rtValue;
			myInstruction.setCycleElapse(1);
			funit.getIntUnit().add(myInstruction);

			/*
			 * if not execution finished then add NOP
			 */
			if(!finishedAdd && !finishedMult && !finishedDiv && !finishedInt && !finishedIntUnit2)
				tempinstr=new Instruction("NOP");
			break;
		case 32: // AND
			rval=reg.readRegisterR(myInstruction.rs) & reg.readRegisterR(myInstruction.rt);
			reg.setWritetoRegisterR(myInstruction.rd, rval);
			//myInstruction.rdValue = myInstruction.rsValue & myInstruction.rtValue;
			myInstruction.setCycleElapse(1);
			funit.getIntUnit().add(myInstruction);

			/*
			 * if not execution finished then add NOP
			 */
			if(!finishedAdd && !finishedMult && !finishedDiv && !finishedInt && !finishedIntUnit2)
				tempinstr=new Instruction("NOP");
			break;
		case 34: // OR
			rval=reg.readRegisterR(myInstruction.rs)|reg.readRegisterR(myInstruction.rt);
			reg.setWritetoRegisterR(myInstruction.rd, rval);
			//myInstruction.rdValue = myInstruction.rsValue | myInstruction.rtValue;
			myInstruction.setCycleElapse(1);
			funit.getIntUnit().add(myInstruction);

			/*
			 * if not execution finished then add NOP
			 */
			if(!finishedAdd && !finishedMult && !finishedDiv && !finishedInt && !finishedIntUnit2)
				tempinstr=new Instruction("NOP");
			break;
		case 40: // DADDI
			rval=reg.readRegisterR(myInstruction.rs)+myInstruction.immediate;
			reg.setWritetoRegisterR(myInstruction.rd, rval);// writeToRegisterR(myInstruction.rd) = myInstruction.rs ^ myInstruction.immediate;
			myInstruction.setCycleElapse(1);
			funit.getIntUnit().add(myInstruction);

			/*
			 * if not execution finished then add NOP
			 */
			if(!finishedAdd && !finishedMult && !finishedDiv && !finishedInt && !finishedIntUnit2)
				tempinstr=new Instruction("NOP");
			break;
		case 42: // DSUBI
			if(reg.readRegisterR(myInstruction.rs)>myInstruction.immediate)
				rval=reg.readRegisterR(myInstruction.rs)-myInstruction.immediate;
			else
				rval=reg.readRegisterR(myInstruction.rs)-myInstruction.immediate;

			reg.setWritetoRegisterR(myInstruction.rd, rval);// writeToRegisterR(myInstruction.rd) = myInstruction.rs ^ myInstruction.immediate;
			/*if (myInstruction.rsValue < myInstruction.rtValue)
				myInstruction.rdValue = 1;
			else
				myInstruction.rdValue = 0;*/

			myInstruction.setCycleElapse(1);
			funit.getIntUnit().add(myInstruction);

			/*
			 * if not execution finished then add NOP
			 */
			if(!finishedAdd && !finishedMult && !finishedDiv && !finishedInt && !finishedIntUnit2)
				tempinstr=new Instruction("NOP");
			break;
		case 44: // ANDI
			/*if (myInstruction.rsValue <= myInstruction.rtValue)
				myInstruction.rdValue = 1;
			else
				myInstruction.rdValue = 0;*/

			rval=reg.readRegisterR(myInstruction.rs)& myInstruction.immediate;
			reg.setWritetoRegisterR(myInstruction.rd, rval);
			myInstruction.setCycleElapse(1);
			funit.getIntUnit().add(myInstruction);

			/*
			 * if not execution finished then add NOP
			 */
			if(!finishedAdd && !finishedMult && !finishedDiv && !finishedInt && !finishedIntUnit2)
				tempinstr=new Instruction("NOP");
			break;
		case 46: // ORI
			rval=reg.readRegisterR(myInstruction.rs)| myInstruction.immediate;
			reg.setWritetoRegisterR(myInstruction.rd, rval);

			myInstruction.setCycleElapse(1);
			funit.getIntUnit().add(myInstruction);

			/*
			 * if not execution finished then add NOP
			 */
			if(!finishedAdd && !finishedMult && !finishedDiv && !finishedInt && !finishedIntUnit2)
				tempinstr=new Instruction("NOP");
			break;
		case 81:
			/*Utilities utils=Utilities.getInstance();
			OutputChart chart=OutputChart.getInstance();

			int instructionid=utils.getInstructionForLabel(myInstruction.getLoopLabel());
			//Registers reg=Registers.getInstance();
				myFetch.PC = instructionid-1; // next instruct will be br addr
				myFetch.myInstruction.flush = true;
				myDecode.myInstruction.flush = true;
				utils.setLoopiteration(utils.getLoopiteration()+1);
				chart.increasesizefornextiteration(utils.getLoopiteration());*/
			break;
		case 82: /*// HLT
			if (myInstruction.rsValue >= myInstruction.rtValue)
				myInstruction.rdValue = 1;
			else
				myInstruction.rdValue = 0;*/
			break;
		case 70: // BEQ
		case 71: // BNE
			/*utils=Utilities.getInstance();
			chart=OutputChart.getInstance();
			instructionid=utils.getInstructionForLabel(myInstruction.getLoopLabel());
			//Registers reg=Registers.getInstance();


			if (((myInstruction.opcode == 71) && (reg.readRegisterR(myInstruction.rd) != reg.readRegisterR(myInstruction.rs)))
					|| ((myInstruction.opcode == 70)&&(reg.readRegisterR(myInstruction.rd) == reg.readRegisterR(myInstruction.rs)))){
				myFetch.PC = instructionid-1; // next instruct will be br addr
				myFetch.myInstruction.flush = true;
				myDecode.myInstruction.flush = true;
				utils.setLoopiteration(utils.getLoopiteration()+1);
				chart.increasesizefornextiteration(utils.getLoopiteration());
			}*/
			break;
		}//end of switcher

		/*
		 * at the end, assign tempinstruction to myInstruction so that when transfering to next stage will do the right thing
		 */
		myInstruction=tempinstr;

		if(!myInstruction.instructionString.equals("NOP")){
			releaseResources();
			if(!(myInstruction.opcode==82||myInstruction.opcode==80||myInstruction.opcode==81||myInstruction.opcode==70||myInstruction.opcode==71)){
				//Utilities utils=Utilities.getInstance();
				//int iteration=utils.getLoopiteration()*Instructions.size();


				int iteration=myInstruction.getIteration_index()*Instructions.size();	
				tablechart=OutputChart.getInstance();
				tablechart.setvalue(myInstruction.getInstruction_index()+iteration+1, 3, Integer.toString(stg.nCycles));

				//System.out.println("instruction="+myInstruction.getInstruction_index()+": Executed at ="+stg.nCycles);
			}
		}

		/**
		 *  relase the resources for next instruction to use
		 *  */

		myMemory.myInstruction = myInstruction;
		myMemory.PC=PC;
		myMemory.step(mainMemory, lb, _stg);
	}

	/*
	 * This function will check if any task at any unit is finished and is so remove that from the unit and set for next setp
	 */
	public void passCompletedTask()
	{
		Instruction tempadderinst,tempmultinst,tempdivinst,tempintinst;
		boolean needToRestartintUnit2=false;
		LRUCache cache=LRUCache.getInstance();
		Registers reg=Registers.getInstance();

		if(funit.getFpMultiplier().size()!=0){
			if(funit.getFpMultiplier().peek().getCycleElapse()>=funit.getFpMultiplierReqCycles() )
			{
				numOfInsReadyToWB=numOfInsReadyToWB+1;
				finishedMult=true;

				//System.out.println(tempmultinst.getInstruction_index()+": finished multiplication execution at="+stg.nCycles);

			}
		}

		/*
		 * this will check if the size ofthe queue is greater than 0
		 * if that then if it completed the required cycles to finish the job
		 * if so, find the destination register value for address to write
		 * if cache hit, then need one cycle
		 * check if memory is busy for other instruction to process or same
		 * if its free then check the address
		 * if miss then update the cache instruction id as current queue top instruction
		 * if memory is busy then check if it finishes required cycle and passed the current units instruction
		 */
		if(funit.getFpDivider().size()!=0){

			if(funit.getFpDivider().peek().getCycleElapse()>=funit.getFpDividerReqCycles() )
			{
				numOfInsReadyToWB=numOfInsReadyToWB+1;
				finishedDiv=true;

			}
		}


		if(funit.getFpAdder().size()!=0){
			if(funit.getFpAdder().peek().getCycleElapse()>=funit.getFpAdderReqCycles() )
			{


				numOfInsReadyToWB=numOfInsReadyToWB+1;
				finishedAdd=true;

			}
		}


		//		-------------------testing loading
		if(funit.getIntUnit2().size()!=0){
			if(funit.getIntUnit2().peek().getCycleElapse()>=(funit.getIntUnit2ReqCycles()) )
			{
				/*
				 * here unit 2 requirements is set 1 as IU takes one, memory is handled later
				 * Still not considered:
				 * if the time expire and the unit is ready so memory is not busy
				 * but it may not get access to to write back
				 */


				Instruction ints=funit.getIntUnit2().peek();
				int address=reg.readRegisterR(ints.rt)+ints.immediate;
				//cache.printlnCache();
				//System.out.println("Request for memory"+reg.readRegisterR(ints.rt)+ints.immediate);

				//for lw and st

				// for L.D and S.W

				/**
				 * first do it for lw and sw
				 */
				if(ints.opcode==48||ints.opcode==54 )
				{

					/**
					 * see if there is structural hazards for LD and SW
					 */
					if(cache.isMemorybusy() && cache.getAccessedmemoryby_instructionid()!=funit.getIntUnit2().peek().getInstruction_index())
					{
						Utilities utils=Utilities.getInstance();
						OutputChart tablechart=OutputChart.getInstance();
						int iteration=utils.getLoopiteration()*instructions.size();
						tablechart.setvalue(funit.getIntUnit2().peek().instruction_index+iteration+1, 8, "Y");
						tablechart.setvalue(ints.instruction_index+iteration+1, 8, "Y");

					}
					if(!cache.isMemorybusy())
					{
						/*if(ints.opcode==54)
						{
							numOfInsReadyToWB=numOfInsReadyToWB+1;
							finishedIntUnit2=true;
						}
						else{*/
						//if(cache.get(reg.readRegisterR(ints.rt)+ints.immediate)!=-1)
						if(cache.isExistsFirstBlock(reg.readRegisterR(ints.rt)+ints.immediate))
						{
							cache.setDataHitOccures(cache.getDataHitOccures()+1);
							cache.setDataRequested(cache.getDataRequested()+1);
							//if(cache.getAccessedmemoryby_instructionid()==funit.getIntUnit2().peek().getInstruction_index()){//if hit occures and the memory access by the same instruction then complete
							numOfInsReadyToWB=numOfInsReadyToWB+1;
							finishedIntUnit2=true;
							//System.out.println(myInstruction.getCycleElapse()+":clock cycle="+stg.nCycles+"Cache hit occures\n");

						}
						else
						{
							cache.setDataMissOccures(cache.getDataMissOccures()+1);
							cache.setDataRequested(cache.getDataRequested()+1);
							cache.loadFromMemToCache(reg.readRegisterR(ints.rt)+ints.immediate);
							//System.out.println(myInstruction.getCycleElapse()+":clock cycle="+stg.nCycles+"=Cache miss occures\n");
							//if miss occures then update instruciton to memory which is in operation currently
							cache.setAccessedmemoryby_instructionid(funit.getIntUnit2().peek().getInstruction_index());
						}
						//}

					}
					else{ //check if memory is busy then whether it is submitted by this instruction and is complete

						if(cache.getAccessedmemoryby_instructionid()==funit.getIntUnit2().peek().getInstruction_index())
						{
							if(cache.isMemoryAccessFinished())
							{
								numOfInsReadyToWB=numOfInsReadyToWB+1;
								finishedIntUnit2=true;
								needToRestartintUnit2=true;
								//System.out.println(myInstruction.getCycleElapse()+":clock cycle="+stg.nCycles+"=memory to cache load finished\n");
								//}
							}
						}

					}
				}
				else if(ints.opcode==56||ints.opcode==58)
				{
					//L.D and S.W
					/**
					 * see if there is structural hazards for LD and SW
					 */
					if(cache.isMemorybusy() && cache.getAccessedmemoryby_instructionid()!=funit.getIntUnit2().peek().getInstruction_index())
					{
						Utilities utils=Utilities.getInstance();
						OutputChart tablechart=OutputChart.getInstance();
						int iteration=utils.getLoopiteration()*instructions.size();
						tablechart.setvalue(funit.getIntUnit2().peek().instruction_index+iteration+1, 8, "Y");
						tablechart.setvalue(ints.instruction_index+iteration+1, 8, "Y");

					}
					if(!cache.isMemorybusy())
					{
						int totaltime=0;
						//if()
						if(!cache.isExistsFirstBlock(address))						
						{
							//if first block not exists
							cache.setDataMissOccures(cache.getDataMissOccures()+1);
							cache.setDataRequested(cache.getDataRequested()+1);

							totaltime=cache.getReqCycleforMiss();
							cache.loadFromMemToCache(address);

							if(!cache.isExistsSecondBlock(address+4)){//if second block not exists
								cache.setDataMissOccures(cache.getDataMissOccures()+1);
								cache.setDataRequested(cache.getDataRequested()+1);

								totaltime=totaltime+cache.getReqCycleforMiss();
								//cache.setReqCycleforMiss(cache.getReqCycleforMiss()*2);
								cache.loadFromMemToCache(address+4);
								//cache.setReqCyclesforHit(cache.getReqCyclesforHit()+1);
							}
							else
							{//if second block exists
								totaltime=totaltime+cache.getReqCyclesforHit();
								cache.setDataHitOccures(cache.getDataHitOccures()+1);
								cache.setDataRequested(cache.getDataRequested()+1);
							}
						}else//if first block found
						{
							totaltime=totaltime+cache.getReqCyclesforHit();
							cache.setDataHitOccures(cache.getDataHitOccures()+1);
							cache.setDataRequested(cache.getDataRequested()+1);

							if(!cache.isExistsSecondBlock(address+4)){//second block miss
								cache.setDataMissOccures(cache.getDataMissOccures()+1);
								cache.setDataRequested(cache.getDataRequested()+1);

								totaltime=totaltime+cache.getReqCycleforMiss();
								//cache.setReqCycleforMiss(cache.getReqCycleforMiss()*2);
								cache.loadFromMemToCache(address+4);
								//cache.setReqCyclesforHit(cache.getReqCyclesforHit()+1);
							}
							else 
							{//second block found
								totaltime=totaltime+cache.getReqCyclesforHit();
								cache.setDataHitOccures(cache.getDataHitOccures()+1);
								cache.setDataRequested(cache.getDataRequested()+1);
							}
						}

						cache.setReqTimeToFinsish(totaltime);
						//if(cache.getElapseCycle()>1)
						cache.setElapseCycle(1);//start accessing memory
						//else cache.setElapseCycle(1);
						cache.setMemorybusy(true);
					}//memory busy if end
					else 
					{
						if(cache.isDoubleMemoryAccessfinished())
						{
							numOfInsReadyToWB=numOfInsReadyToWB+1;
							finishedIntUnit2=true;
							needToRestartintUnit2=true;
							//System.out.println(myInstruction.getCycleElapse()+":clock cycle="+stg.nCycles+"=memory to cache load finished\n");
							//}
						}
					}//end of else of memorybusy
				}//end of if opcode
			}
		}



		//-------------------end of testing loading
		//		
		//		if(funit.getIntUnit2().size()!=0){
		//			if(funit.getIntUnit2().peek().getCycleElapse()>=(funit.getIntUnit2ReqCycles()) )
		//			{
		//				//numOfInsReadyToWB=numOfInsReadyToWB+1;
		//				//finishedIntUnit2=true;
		//
		//				/*
		//				 * Still not considered:
		//				 * if the time expire and the unit is ready so memory is not busy
		//				 * but it may not get access to to write back
		//				 */
		//				Instruction ints=funit.getIntUnit2().peek();
		//				cache.printlnCache();
		//				System.out.println("Request for memory"+reg.readRegisterR(ints.rt)+ints.immediate);
		//				if(!cache.isMemorybusy())
		//				{
		//					if(cache.get(reg.readRegisterR(ints.rt)+ints.immediate)!=-1)
		//					{
		//						//if(cache.getAccessedmemoryby_instructionid()==funit.getIntUnit2().peek().getInstruction_index()){//if hit occures and the memory access by the same instruction then complete
		//						numOfInsReadyToWB=numOfInsReadyToWB+1;
		//						finishedIntUnit2=true;
		//						System.out.println(myInstruction.getCycleElapse()+":clock cycle="+stg.nCycles+"Cache hit occures\n");
		//						//}
		//						//						else
		//						//						{
		//						//							//if others updated the instruction then wait
		//						//							cache.setAccessedmemoryby_instructionid(funit.getIntUnit2().peek().getInstruction_index());
		//						//						}
		//					}
		//					else
		//					{
		//						System.out.println(myInstruction.getCycleElapse()+":clock cycle="+stg.nCycles+"=Cache miss occures\n");
		//						//if miss occures then update instruciton to memory which is in operation currently
		//						cache.setAccessedmemoryby_instructionid(funit.getIntUnit2().peek().getInstruction_index());
		//					}
		//
		//				}
		//				else{ //check if memory is busy then whether it is submitted by this instruction and is complete
		//
		//					if(cache.getAccessedmemoryby_instructionid()==funit.getIntUnit2().peek().getInstruction_index())
		//					{
		//						if(cache.isMemoryAccessFinished())
		//						{
		//							/*if(myInstruction.opcode==56 || myInstruction.opcode==58)
		//							{
		//								if(cache.get(reg.readRegisterR(ints.rt)+ints.immediate+4)!=-1)
		//								{
		//									//if(cache.getAccessedmemoryby_instructionid()==funit.getIntUnit2().peek().getInstruction_index()){//if hit occures and the memory access by the same instruction then complete
		//									numOfInsReadyToWB=numOfInsReadyToWB+1;
		//									finishedIntUnit2=true;
		//									System.out.println(myInstruction.getCycleElapse()+":clock cycle="+stg.nCycles+"Cache hit occures\n");
		//									//}
		//									//									else
		//									//									{
		//									//										//if others updated the instruction then wait
		//									//										cache.setAccessedmemoryby_instructionid(funit.getIntUnit2().peek().getInstruction_index());
		//									//									}
		//								}
		//								else
		//								{
		//									System.out.println(myInstruction.getCycleElapse()+":clock cycle="+stg.nCycles+"=Cache miss occures\n");
		//									//if miss occures then update instruciton to memory which is in operation currently
		//									cache.setAccessedmemoryby_instructionid(funit.getIntUnit2().peek().getInstruction_index());
		//								}
		//
		//							}
		//							else{
		//*/								numOfInsReadyToWB=numOfInsReadyToWB+1;
		//								finishedIntUnit2=true;
		//								needToRestartintUnit2=true;
		//								System.out.println(myInstruction.getCycleElapse()+":clock cycle="+stg.nCycles+"=memory to cache load finished\n");
		//							//}
		//						}
		//					}
		//
		//				}
		//
		//
		//			}
		//		}

		if(funit.getIntUnit().size()!=0){
			if(funit.getIntUnit().peek().getCycleElapse()>=funit.getintUnitReqCycles() )
			{
				numOfInsReadyToWB=numOfInsReadyToWB+1;
				finishedInt=true;


			}
		}


		/*
		 * if multiple instruction are ready to write
		 *
		 */
		if(numOfInsReadyToWB==1)
		{
			if(finishedAdd)
			{
				tempadderinst=funit.getFpAdder().poll();
				tempinstr=tempadderinst;
				//System.out.println(tempadderinst.getInstruction_index()+": finished add execution at="+stg.nCycles);
			}
			else if(finishedDiv)
			{

				tempdivinst=funit.getFpDivider().poll();
				tempinstr=tempdivinst;
				//System.out.println(tempdivinst.getInstruction_index()+": finished divided operation at="+stg.nCycles);

			}
			else if(finishedMult)
			{
				tempmultinst=funit.getFpMultiplier().poll();
				tempinstr=tempmultinst;

				//System.out.println(tempmultinst.getInstruction_index()+": finished multiplication execution at="+stg.nCycles);
			}
			else if(finishedInt)
			{
				tempintinst=funit.getIntUnit().poll();
				tempinstr=tempintinst;

				//System.out.println(tempintinst.getInstruction_index()+": finished intoperation execution at="+stg.nCycles);
			}
			else if(finishedIntUnit2)
			{
				tempintinst=funit.getIntUnit2().poll();
				if(tempintinst.opcode==48){ //48 lw L.D
					int rval=reg.readRegisterR(tempintinst.rt)+tempintinst.immediate;//-reg.readRegisterR(myInstruction.rt);
					rval=cache.get(rval);
					reg.setWritetoRegisterR(tempintinst.rd, rval);
				}

				if(tempintinst.opcode==54)
				{
					int address=reg.readRegisterR(tempintinst.rt)+tempintinst.immediate;//-reg.readRegisterR(myInstruction.rt);
					int rval=reg.readRegisterR(tempintinst.rd);//-reg.readRegisterR(myInstruction.rt);
					cache.updateCacheValue(address, rval);
				}
				tempinstr=tempintinst;
				if(funit.getIntUnit2().size()>0)
					funit.getIntUnit2().peek().setCycleElapse(0);
				//System.out.println(tempintinst.getInstruction_index()+": finished multiplication execution at="+stg.nCycles);
			}
		}
		else if(numOfInsReadyToWB>1)
		{
			PriorityOfFunctionalUnits obj;
			PQsort pqs = new PQsort();
			Utilities utils=Utilities.getInstance();
			OutputChart tablechart=OutputChart.getInstance();
			int iteration=utils.getLoopiteration()*instructions.size();
			//tablechart=OutputChart.getInstance();


			PriorityQueue<PriorityOfFunctionalUnits> nonPiplined=new PriorityQueue<PriorityOfFunctionalUnits>(4,pqs);
			PriorityQueue<PriorityOfFunctionalUnits> piplined=new PriorityQueue<PriorityOfFunctionalUnits>(4,pqs);
			// if anyone is not piplined
			//if(!funit.isIsfpAdderPiplined() || !funit.isIsfpDividerPiplined() || !funit.isIsfpMultiplierPiplined() /*|| !funit.isIsintUnitPiplined()*/)
			//{

			//---------adder
			if(!funit.isIsfpAdderPiplined() && finishedAdd)
			{
				tablechart.setvalue(funit.getFpAdder().peek().instruction_index+iteration+1, 8, "Y");
				obj=new PriorityOfFunctionalUnits("fpadder",funit.getFpAdderReqCycles(),funit.isIsfpAdderPiplined());
				nonPiplined.add(obj);
			}
			if(funit.isIsfpAdderPiplined() && finishedAdd)
			{
				tablechart.setvalue(funit.getFpAdder().peek().instruction_index+iteration+1, 8, "Y");
				obj=new PriorityOfFunctionalUnits("fpadder",funit.getFpAdderReqCycles(),funit.isIsfpAdderPiplined());
				piplined.add(obj);
			}
			//88----------------divider
			if(!funit.isIsfpDividerPiplined() && finishedDiv)
			{
				tablechart.setvalue(funit.getFpDivider().peek().instruction_index+iteration+1, 8, "Y");
				obj=new PriorityOfFunctionalUnits("fpdivider",funit.getFpDividerReqCycles(),funit.isIsfpDividerPiplined());
				nonPiplined.add(obj);
			}
			if(funit.isIsfpDividerPiplined() && finishedDiv)
			{
				tablechart.setvalue(funit.getFpDivider().peek().instruction_index+iteration+1, 8, "Y");
				obj=new PriorityOfFunctionalUnits("fpdivider",funit.getFpDividerReqCycles(),funit.isIsfpDividerPiplined());
				piplined.add(obj);
			}
			//-----------------multiplier------------
			if(!funit.isIsfpMultiplierPiplined() && finishedMult)
			{
				tablechart.setvalue(funit.getFpMultiplier().peek().instruction_index+iteration+1, 8, "Y");
				obj=new PriorityOfFunctionalUnits("fpmultiplier",funit.getFpMultiplierReqCycles(),funit.isIsfpMultiplierPiplined());
				nonPiplined.add(obj);
			}
			if(funit.isIsfpMultiplierPiplined() && finishedMult)
			{
				tablechart.setvalue(funit.getFpMultiplier().peek().instruction_index+iteration+1, 8, "Y");
				obj=new PriorityOfFunctionalUnits("fpmultiplier",funit.getFpMultiplierReqCycles(),funit.isIsfpMultiplierPiplined());
				piplined.add(obj);
			}

			//----------------Int unit----------------------------
			if(!funit.isIsintUnitPiplined() && finishedInt)
			{
				tablechart.setvalue(funit.getIntUnit().peek().instruction_index+iteration+1, 8, "Y");
				obj=new PriorityOfFunctionalUnits("intunit",funit.getintUnitReqCycles(),funit.isIsintUnitPiplined());
				nonPiplined.add(obj);
			}
			if(funit.isIsintUnitPiplined() && finishedInt)
			{
				tablechart.setvalue(funit.getIntUnit().peek().instruction_index+iteration+1, 8, "Y");
				obj=new PriorityOfFunctionalUnits("intunit",funit.getintUnitReqCycles(),funit.isIsintUnitPiplined());
				piplined.add(obj);
			}
			// intunit2
			if(!funit.isIsinitUnit2Piplined() && finishedIntUnit2)
			{
				tablechart.setvalue(funit.getIntUnit2().peek().instruction_index+iteration+1, 8, "Y");
				obj=new PriorityOfFunctionalUnits("intunit2",funit.getIntUnit2ReqCycles(),funit.isIsinitUnit2Piplined());
				nonPiplined.add(obj);
			}
			if(funit.isIsinitUnit2Piplined() && finishedIntUnit2)
			{
				tablechart.setvalue(funit.getIntUnit2().peek().instruction_index+iteration+1, 8, "Y");
				obj=new PriorityOfFunctionalUnits("intunit2",funit.getIntUnit2ReqCycles(),funit.isIsinitUnit2Piplined());
				piplined.add(obj);
			}


			/*
			 * Determine which has first access
			 */

			if(!nonPiplined.isEmpty())
			{
				if(nonPiplined.peek().funitname.equals("fpadder"))
				{
					tempadderinst=funit.getFpAdder().poll();
					tempinstr=tempadderinst;
					//System.out.println(tempadderinst.getInstruction_index()+": finished add execution at="+stg.nCycles);
				}
				else if(nonPiplined.peek().funitname.equals("fpdivider"))
				{

					tempdivinst=funit.getFpDivider().poll();
					tempinstr=tempdivinst;
					//System.out.println(tempdivinst.getInstruction_index()+": finished divided operation at="+stg.nCycles);

				}
				else if(nonPiplined.peek().funitname.equals("fpmultiplier"))
				{
					tempmultinst=funit.getFpMultiplier().poll();
					tempinstr=tempmultinst;

					//System.out.println(tempmultinst.getInstruction_index()+": finished multiplication execution at="+stg.nCycles);
				}
				else if(nonPiplined.peek().funitname.equals("intunit"))
				{
					tempintinst=funit.getIntUnit().poll();
					tempinstr=tempintinst;

					//System.out.println(tempintinst.getInstruction_index()+": finished multiplication execution at="+stg.nCycles);
				}
				else if(nonPiplined.peek().funitname.equals("intunit2"))
				{
					tempintinst=funit.getIntUnit2().poll();
					/*	if(cache.getAccessedmemoryby_instructionid()==tempintinst.getInstruction_index())
					{
					cache.setMemorybusy(false);
					}*/
					/*case 48: // LW
				case 56: //L.D
				case 58: //S.W
				case 54: // SW
					 */					
					if(tempintinst.opcode==48){ //48 lw L.D
						int rval=reg.readRegisterR(tempintinst.rt)+tempintinst.immediate;//-reg.readRegisterR(myInstruction.rt);
						rval=cache.get(rval);
						reg.setWritetoRegisterR(tempintinst.rd, rval);
					}

					if(tempintinst.opcode==54)
					{
						int address=reg.readRegisterR(tempintinst.rt)+tempintinst.immediate;//-reg.readRegisterR(myInstruction.rt);
						int rval=reg.readRegisterR(tempintinst.rd);//-reg.readRegisterR(myInstruction.rt);
						cache.updateCacheValue(address, rval);
					}

					tempinstr=tempintinst;
					if(funit.getIntUnit2().size()>0)
						funit.getIntUnit2().peek().setCycleElapse(0);
					//System.out.println(tempintinst.getInstruction_index()+": finished multiplication execution at="+stg.nCycles);
				}
			}
			else if(!piplined.isEmpty())
			{
				if(piplined.peek().funitname.equals("fpadder"))
				{
					tempadderinst=funit.getFpAdder().poll();
					tempinstr=tempadderinst;
					//System.out.println(tempadderinst.getInstruction_index()+": finished add execution at="+stg.nCycles);
				}
				else if(piplined.peek().funitname.equals("fpdivider"))
				{

					tempdivinst=funit.getFpDivider().poll();
					tempinstr=tempdivinst;
					//System.out.println(tempdivinst.getInstruction_index()+": finished divided operation at="+stg.nCycles);

				}
				else if(piplined.peek().funitname.equals("fpmultiplier"))
				{
					tempmultinst=funit.getFpMultiplier().poll();
					tempinstr=tempmultinst;

					//System.out.println(tempmultinst.getInstruction_index()+": finished multiplication execution at="+stg.nCycles);
				}
				else if(piplined.peek().funitname.equals("intunit"))
				{
					tempintinst=funit.getIntUnit().poll();
					tempinstr=tempintinst;

					//System.out.println(tempintinst.getInstruction_index()+": finished multiplication execution at="+stg.nCycles);
				}
				else if(piplined.peek().funitname.equals("intunit2"))
				{
					tempintinst=funit.getIntUnit2().poll();
					if(tempintinst.opcode==48){ //48 lw L.D
						int rval=reg.readRegisterR(tempintinst.rt)+tempintinst.immediate;//-reg.readRegisterR(myInstruction.rt);
						rval=cache.get(rval);
						reg.setWritetoRegisterR(tempintinst.rd, rval);
					}

					if(tempintinst.opcode==54)
					{
						int address=reg.readRegisterR(tempintinst.rt)+tempintinst.immediate;//-reg.readRegisterR(myInstruction.rt);
						int rval=reg.readRegisterR(tempintinst.rd);//-reg.readRegisterR(myInstruction.rt);
						cache.updateCacheValue(address, rval);
					}
					tempinstr=tempintinst;
					if(funit.getIntUnit2().size()>0)
						funit.getIntUnit2().peek().setCycleElapse(0);
					//System.out.println(tempintinst.getInstruction_index()+": finished multiplication execution at="+stg.nCycles);
				}
			}

		}
		// End of wb multiple entry checking



	}
	public void updateCycles()
	{
		Queue<Instruction> tempqueue=new LinkedList<Instruction>();
		/*
		 * for adder
		 */
		//Queue<Instruction> fpadder=funit.getFpAdder();

		for(Instruction inst:funit.getFpAdder())
		{
			inst.setCycleElapse(inst.getCycleElapse()+1);
			tempqueue.add(inst);
		}
		funit.setFpAdder(tempqueue);
		/*
		 * for fpmultiplier
		 */
		tempqueue=new LinkedList<Instruction>();
		for(Instruction inst:funit.getFpMultiplier())
		{
			inst.setCycleElapse(inst.getCycleElapse()+1);
			tempqueue.add(inst);
		}
		funit.setFpMultiplier(tempqueue);

		/*
		 * for divider
		 */
		tempqueue=new LinkedList<Instruction>();
		for(Instruction inst:funit.getFpDivider())
		{
			inst.setCycleElapse(inst.getCycleElapse()+1);
			tempqueue.add(inst);
		}
		funit.setFpDivider(tempqueue);

		/*
		 * integer unit
		 */
		tempqueue=new LinkedList<Instruction>();
		for(Instruction inst:funit.getIntUnit())
		{
			inst.setCycleElapse(inst.getCycleElapse()+1);
			tempqueue.add(inst);
		}
		funit.setIntUnit(tempqueue);

		/*
		 * integer unit
		 */
		tempqueue=new LinkedList<Instruction>();

		for(Instruction inst:funit.getIntUnit2())
		{
			inst.setCycleElapse(inst.getCycleElapse()+1);
			tempqueue.add(inst);
		}
		funit.setIntUnit2(tempqueue);

		//Cachememory cycle update if its memory access is underway
		LRUCache cache=LRUCache.getInstance();
		if(cache.isMemorybusy())
		{
			cache.setElapseCycle(cache.getElapseCycle()+1);
		}
	}
	/**
	 * returns a string representation of the current instructions and the results of its processing.
	 */
	public String toString() {
		String temp;

		if (myInstruction.flush == true) {
			temp = "FLUSHED: \n" + myInstruction + "\n";
			return temp;
		}

		switch(myInstruction.opcode) {
		case 0:
			temp = myInstruction + "\n";
			break;
		case 35:  // LW
		case 43:  // SW
			temp = Integer.toString(PC) + ":\n" + myInstruction + "\n" +
					"Memory\nAddress= " + Integer.toString(myInstruction.rdValue) + "\n";
			break;
		case 70:  // BEQ
			if (myInstruction.rsValue == myInstruction.rtValue)
				temp = Integer.toString(PC) + ":\n" + myInstruction + "\n"+
						"Result:\nTake branch\n";
			else
				temp = Integer.toString(PC) + ":\n" + myInstruction + "\n"+
						"Result:\nDon't take\nbranch\n";
			break;
		case 71:  // BNE
			if (myInstruction.rsValue != myInstruction.rtValue)
				temp = Integer.toString(PC) + ":\n" + myInstruction + "\n"+
						"Result:\nTake branch\n";
			else
				temp = Integer.toString(PC) + ":\n" + myInstruction + "\n"+
						"Result:\nDon't take\nbranch\n";
			break;
		default:  // R-type instructions
			temp = Integer.toString(PC) + ":\n" + myInstruction + "\n" +
					"ALUop1= " + Integer.toString(myInstruction.rsValue) + "\n" +
					"ALUop2= " + Integer.toString(myInstruction.rtValue) + "\n" +
					"Result= " + Integer.toString(myInstruction.rdValue) + "\n";
		}
		return temp;
	}

	public void releaseResources(){
		/**
		 *REGISTER FORMAT
		 * LW OR SW DESTINATION VALUE(SOURCE) AND DEFINED BY rd and rt
		 * DADDI F1,F2,4 rd=F1, rs=F2
		 * ADD.D F1,F2,F3 F1=rd,rs=F2, rt=F3
		 */

		Registers reg=Registers.getInstance();
		if((myInstruction.opcode==24||myInstruction.opcode==26||myInstruction.opcode==32
				||myInstruction.opcode==34||myInstruction.opcode==36||myInstruction.opcode==38
				||myInstruction.opcode==50||myInstruction.opcode==51))
		{
			//DSUB=24,DADD=26,AND=32 OR=34 ADD.D=36 MUL.D=38 DIV.D=50 SUB.D=51
			if(myInstruction.rdtype==regType.R)
			{
				reg.setIsRegisterBusyR(myInstruction.rd, false);
				reg.setRMode(myInstruction.rd, registerMode.Idle);
			}
			else 
			{
				reg.setIsRegisterBusyF(myInstruction.rd, false);
				reg.setFMode(myInstruction.rd, registerMode.Idle);
			}
			/*if(myInstruction.rstype==regType.R)
				reg.setIsRegisterBusyR(myInstruction.rs, false);
			else reg.setIsRegisterBusyF(myInstruction.rs, false);

			if(myInstruction.rttype==regType.R)
				reg.setIsRegisterBusyR(myInstruction.rt, false);
			else reg.setIsRegisterBusyF(myInstruction.rt, false);*/
		}
		else if(myInstruction.opcode==40||myInstruction.opcode==42
				||myInstruction.opcode==44||myInstruction.opcode==46)
		{
			//DADDI=40 DSUBI=42 ANDI=44 ORI=46
			if(myInstruction.rdtype==regType.R)
			{
				reg.setIsRegisterBusyR(myInstruction.rd, false);
				reg.setRMode(myInstruction.rd, registerMode.Idle);
			}
			else 
			{
				reg.setIsRegisterBusyF(myInstruction.rd, false);
				reg.setFMode(myInstruction.rd, registerMode.Idle);
			}
			/*if(myInstruction.rstype==regType.R)
				reg.setIsRegisterBusyR(myInstruction.rs, false);
			else reg.setIsRegisterBusyF(myInstruction.rs, false);
			 */

		}
		else if(myInstruction.opcode==48||myInstruction.opcode==54
				||myInstruction.opcode==56||myInstruction.opcode==58)
		{
			//LW=48 SW=54 L.D=56 S.W=58
			if(myInstruction.rdtype==regType.R)
			{
				reg.setRMode(myInstruction.rd, registerMode.Idle);
				reg.setIsRegisterBusyR(myInstruction.rd, false);
			}
			else 
			{
				reg.setFMode(myInstruction.rd, registerMode.Idle);
				reg.setIsRegisterBusyF(myInstruction.rd, false);
			}


			/*if(myInstruction.rttype==regType.R)
				reg.setIsRegisterBusyR(myInstruction.rt, false);
			else reg.setIsRegisterBusyF(myInstruction.rt, false);*/
		}
	}

	static class PQsort implements Comparator<PriorityOfFunctionalUnits> {

		public int compare(PriorityOfFunctionalUnits one, PriorityOfFunctionalUnits two) {
			return two.getReqcycles() - one.getReqcycles();
		}
	}
}

